﻿using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;

public class TestSceneEditorWindow : EditorWindow {

    public float percentBack = 0.05f;
    public float percentSpacing = 0.02f;
    public GUIStyle displayStyle;

    private string strNumSplits = string.Empty;
    private string strDungeonSize = string.Empty;

    private int numSplits = 4;
    private int dungeonSize = 256;

    public int minPartitionDimensions = 16;
    public float minSplitRatio = 0.3f;
    public float maxSplitRatio = 0.7f;
    public int minRoomMargin = 1;
    public float minRoomDimensions = 0.55f;
    public float maxRoomDimensions = 0.9f;

    private Dungeon dungeon;
    private List<Room> rooms = new List<Room>();
    private List<Corridor> corridors = new List<Corridor>();

    public Color backgroundColor;
    public Color oldSplitColor;
    public Color newSplitColor;
    public Color roomColor;
    public Color corridorColor;

    private Texture2D backgroundTexture;
    private Texture2D oldSplitTexture;
    private Texture2D newSplitTexture;
    private Texture2D roomTexture;
    private Texture2D corridorTexture;

    private bool showCorridors = false;

    [MenuItem("Level Generation/Generator")]
    public static void Launch2DWindow()
    {
        EditorWindow.GetWindow<TestSceneEditorWindow>();
    }

    public void DoInit()
    {
        displayStyle = GUIStyle.none;

        backgroundTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
        oldSplitTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
        newSplitTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
        roomTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
        corridorTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);

        backgroundColor = new Color32(116, 116, 116, 255);
        oldSplitColor = new Color32(49, 29, 255, 255);
        newSplitColor = new Color32(234, 44, 44, 255);
        roomColor = new Color32(59, 213, 84, 255);
        corridorColor = new Color(255, 255, 255, 255);

        backgroundTexture.SetPixel(0, 0, backgroundColor);
        oldSplitTexture.SetPixel(0, 0, oldSplitColor);
        newSplitTexture.SetPixel(0, 0, newSplitColor);
        roomTexture.SetPixel(0, 0, roomColor);
        corridorTexture.SetPixel(0, 0, corridorColor);

        backgroundTexture.Apply();
        oldSplitTexture.Apply();
        newSplitTexture.Apply();
        roomTexture.Apply();
        corridorTexture.Apply();

        InitializeDungeon(dungeonSize);
    }

    public void Render()
    {
        // Draw the background in the appropriate size,
        // Draw the old splits in the old split color,
        // Draw the new splits in the new split color.
        // This is easily done in that all new splits will be
        // one above the child level.

        int xOffset = 100;
        int yOffset = 100;

        // Draw background
        Graphics.DrawTexture(new Rect(xOffset, yOffset, dungeon.Size, dungeon.Size), backgroundTexture);

        // Draw border of dungeon in lines, using old split color
        if (!showCorridors)
        {
            Graphics.DrawTexture(new Rect(xOffset, yOffset, 1, dungeon.Size), oldSplitTexture);
            Graphics.DrawTexture(new Rect(xOffset, yOffset, dungeon.Size, 1), oldSplitTexture);
            Graphics.DrawTexture(new Rect(xOffset + dungeon.Size, yOffset, 1, dungeon.Size), oldSplitTexture);
            Graphics.DrawTexture(new Rect(xOffset, yOffset + dungeon.Size, dungeon.Size, 1), oldSplitTexture);

            // Iterate through the dungeon partitions
            //      Use the node data to find space data - each node has a location, a width, and a height
            //      Each node may also have splitting information
            List<DungeonPartition> newPartitions = dungeon.GetLatestPartitions();

            foreach (DungeonPartition newPartition in newPartitions)
            {
                switch (newPartition.Split)
                {
                    case DungeonPartition.SplitType.Horizontal:
                        Graphics.DrawTexture(new Rect(xOffset + newPartition.X, yOffset + newPartition.Y + newPartition.Left.Height, newPartition.Width, 1), newSplitTexture);
                        break;
                    case DungeonPartition.SplitType.Vertical:
                        Graphics.DrawTexture(new Rect(xOffset + newPartition.X + newPartition.Left.Width, yOffset + newPartition.Y, 1, newPartition.Height), newSplitTexture);
                        break;
                }
            }

            List<DungeonPartition> oldPartitions = dungeon.GetAllButLatestPartitions();
            foreach (DungeonPartition oldPartition in oldPartitions)
            {
                switch (oldPartition.Split)
                {
                    case DungeonPartition.SplitType.Horizontal:
                        Graphics.DrawTexture(new Rect(xOffset + oldPartition.X, yOffset + oldPartition.Y + oldPartition.Left.Height, oldPartition.Width, 1), oldSplitTexture);
                        break;
                    case DungeonPartition.SplitType.Vertical:
                        Graphics.DrawTexture(new Rect(xOffset + oldPartition.X + oldPartition.Left.Width, yOffset + oldPartition.Y, 1, oldPartition.Height), oldSplitTexture);
                        break;
                }
            }
        }

        foreach (Room room in rooms)
        {
            Graphics.DrawTexture(new Rect(xOffset + room.X, yOffset + room.Y, room.Width, room.Height), roomTexture);
        }

        if (showCorridors)
        {
            foreach (Corridor corridor in corridors)
            {
                switch (corridor.Type)
                {
                    case Corridor.CorridorType.Horizontal:
                        Graphics.DrawTexture(new Rect(xOffset + corridor.MinX, yOffset + corridor.MinY, corridor.MaxX - corridor.MinX + 1, 1), corridorTexture);
                        break;
                    case Corridor.CorridorType.Vertical:
                        Graphics.DrawTexture(new Rect(xOffset + corridor.MinX, yOffset + corridor.MinY, 1, corridor.MaxY - corridor.MinY + 1), corridorTexture);
                        break;
                }
            }
        }
    }

    #region Room methods

    private void InitializeDungeon(int size)
    {
        MonoBehaviour.print("initializing new dungeon");
        dungeon = new Dungeon(size, minPartitionDimensions, minSplitRatio, maxSplitRatio, minRoomMargin, minRoomDimensions, maxRoomDimensions);
    }

    #endregion

    void OnGUI()
    {
        if (dungeon == null) DoInit();

        if (GUI.Button(new Rect(5, 5, 80, 20), "Split Once"))
        {
            // Partition all child nodes once.
            int times = dungeon.SplitAll();
            MonoBehaviour.print("Dungeon split " + times.ToString() + " times");
            rooms = new List<Room>();
            corridors = new List<Corridor>();
            showCorridors = false;
        }

        GUI.Label(new Rect(5, 30, 60, 20), "# Splits", displayStyle);
        strNumSplits = GUI.TextField(new Rect(70, 30, 60, 20), numSplits.ToString(), 1);
        int.TryParse(strNumSplits, out numSplits);

        if (GUI.Button(new Rect(135, 30, 70, 20), "Split All"))
        {
            // Iteratively parse all child nodes a certain number of times.
            rooms = new List<Room>();
            corridors = new List<Corridor>();
            showCorridors = false;
            for (int i = 0; i < numSplits; i++)
                dungeon.SplitAll();
        }


        GUI.Label(new Rect(5, 65, 90, 20), "Dungeon Size", displayStyle);
        strDungeonSize = GUI.TextField(new Rect(100, 65, 30, 20), dungeonSize.ToString(), 3);
        int.TryParse(strDungeonSize, out dungeonSize);
        if (GUI.Button(new Rect(135, 65, 50, 20), "Set"))
        {
            // Set dungeon size and clear all splits.
            InitializeDungeon(dungeonSize);
            MonoBehaviour.print("spawned new dungeon");
            rooms = new List<Room>();
            corridors = new List<Corridor>();
            showCorridors = false;
        }

        if (GUI.Button(new Rect(220, 5, 120, 20), "Generate Rooms"))
        {
            rooms = dungeon.GenerateRooms();
            corridors = new List<Corridor>();
            showCorridors = false;
            MonoBehaviour.print(string.Format("{0} rooms generated", rooms.Count));
        }

        if (GUI.Button(new Rect(345, 5, 150, 20), "Generate Corridors"))
        {
            if (rooms.Count > 0)
            {
                corridors = dungeon.GenerateCorridors();
                showCorridors = true;
                MonoBehaviour.print(string.Format("{0} corridors generated", corridors.Count));

                // TODO: Save tile data to disk.
                int[] tileData = new int[dungeonSize * dungeonSize];
                for (int y = 0; y < dungeonSize; y++)
                {
                    for (int x = 0; x < dungeonSize; x++)
                    {
                        tileData[y * dungeonSize + x] = 0;
                    }
                }

                // Open rooms
                foreach (Room room in rooms)
                {
                    for (int y = 0; y < room.Height; y++)
                    {
                        for (int x = 0; x < room.Width; x++)
                        {
                            tileData[(room.Y + y) * dungeonSize + room.X + x] = 1;
                        }
                    }
                }

                // Open corridors
                foreach (Corridor corridor in corridors)
                {
                    for (int y = corridor.MinY; y <= corridor.MaxY; y++)
                        for (int x = corridor.MinX; x <= corridor.MaxX; x++)
                            tileData[y * dungeonSize + x] = 1;
                }

                // TODO: Save
                DungeonIndexer indexer = new DungeonIndexer(dungeon);

            }
            else
            {
                MonoBehaviour.print("No rooms generated yet");
            }
        }

        if (GUI.Button(new Rect(345, 30, 150, 20), "Show/Hide Corridors"))
        {
            if (corridors.Count > 0)
            {
                showCorridors = !showCorridors;
            }
            else
            {
                MonoBehaviour.print("No corridors generated yet");
            }
        }

        if (GUI.Button(new Rect(220, 30, 80, 20), "Do All"))
        {
            InitializeDungeon(dungeonSize);
            for (int i = 0; i < numSplits; i++)
                dungeon.SplitAll();
            rooms = dungeon.GenerateRooms();
            MonoBehaviour.print(string.Format("{0} rooms generated", rooms.Count));
            corridors = new List<Corridor>();
            showCorridors = false;
        }

        if (Event.current.type == EventType.repaint)
        {
            MonoBehaviour.print("Rendering");
            Render();
        }
    }


}
